package org.gavin.core.conf;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.extern.slf4j.Slf4j;
import org.gavin.core.annotation.FieldMask;
import org.gavin.core.constant.CommonConst;
import org.gavin.core.utils.DesensitizedUtil;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SensitiveJsonSerializer
 * 自定义敏感字段JSON序列化器，用于对敏感字段进行脱敏处理
 *
 * @author grl
 * @date 2024/1/11
 */
@Slf4j
@Component
public class SensitiveJsonSerializer extends JsonSerializer<Object> implements ContextualSerializer {

    /**
     * 用于保存脱敏类型的私有字段
     */
    public static Map<String, Map<String, Integer>> sensitive = new ConcurrentHashMap<>(128);

    /**
     * 序列化方法，将字段值进行脱敏处理或直接输出原始值。
     *
     * @param value       待序列化的字段值
     * @param gen         JsonGenerator实例
     * @param serializers SerializerProvider实例
     * @throws IOException 如果序列化过程中发生IO错误
     */

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        Class<?> aClass = gen.getCurrentValue().getClass();
        String className = aClass.getName();
        String filedName = gen.getOutputContext().getCurrentName();
        String key = className + "#" + filedName;
        Map<String, Integer> localMap = sensitive.get(key);
        if (value != null && localMap != null) {
            String valueStr;
            if (value instanceof String str) {
                valueStr = str;
            } else if (value instanceof Number) {
                valueStr = String.valueOf(value);
            } else {
                valueStr = "*";
            }
            Integer prefix = localMap.get(CommonConst.FIELD_MASK_PREFIX);
            Integer suffix = localMap.get(CommonConst.FIELD_MASK_SUFFIX);
            if (CommonConst.ZERO.equals(prefix) && CommonConst.ZERO.equals(suffix)) {
                gen.writeObject(DesensitizedUtil.password(valueStr));
            } else {
                gen.writeObject(DesensitizedUtil.idCardNum(valueStr, prefix, suffix));
            }
        } else {
            gen.writeObject(value);
        }
        log.debug("序列化的线程================={}", Thread.currentThread().getName());
    }

    /**
     * 上下文创建方法，根据注解和类型动态配置序列化器实例。
     *
     * @param prov     SerializerProvider实例
     * @param property BeanProperty实例
     * @return 创建的序列化器实例
     * @throws JsonMappingException 如果发生JSON映射错误
     */

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        FieldMask annotation = property.getAnnotation(FieldMask.class);
        if (Objects.nonNull(annotation)) {
            Class<?> declaringClass = property.getMember().getDeclaringClass();
            String className = declaringClass.getName();
            String filedName = property.getFullName().getSimpleName();
            String key = className + "#" + filedName;
            int prefix = annotation.prefix();
            int suffix = annotation.suffix();
            Map<String, Integer> localMap = sensitive.computeIfAbsent(key, k -> new HashMap<>(4));
            localMap.put(CommonConst.FIELD_MASK_PREFIX, prefix);
            localMap.put(CommonConst.FIELD_MASK_SUFFIX, suffix);
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}
